iT邦幫忙

2021 iThome 鐵人賽

DAY 28
0
Software Development

【今年還是不夠錢買psQQ,不如我們用PyQt自己寫一個】系列 第 28

【沒錢買ps,PyQt自己寫】Day 28 - final project - 1 / 來搞一個自己的 photoshop 吧!UI 篇 + 純程式架構篇 (結合 PyQt + OpenCV)

  • 分享至 

  • xImage
  •  

看完這篇文章你會得到的成果圖

此篇文章的範例程式碼 github

https://github.com/howarder3/ironman2021_PyQt5_photoshop/tree/main/day28_final_project_day1

之前內容的重點複習 (前情提要)

我們接下來的討論,會基於讀者已經先讀過我 day5 文章 的架構下去進行程式設計
如果還不清楚我程式設計的邏輯 (UI.py、controller.py、start.py 分別在幹麻)
建議先閱讀 day5 文章後再來閱讀此文。

https://www.wongwonggoods.com/python/pyqt5-5/

設計我們的 UI

雖然原本我有想直接拿 Day17 的內容繼續改,
後來覺得架構上還不夠漂亮,最後決定乾脆直接砍掉重練比較快哈哈哈。

既然要搞就一次搞到最大吧!

這次我設計的 final project UI 初版長這樣

欸等等先別吐血啊!!! 這 UI 不是一天搞出來的啊!!
UI 就請大家自己慢慢刻哈哈哈哈哈!!!

(你說突然這也突然跳太高階了吧?!
其實只是設定一些顏色而已XD,之前因為這個相對簡單就沒特別介紹)

排版控制 Layout 系列

如果還有機會的話有空在介紹,不過這邊的內容多半是用於排版,
所以也難能以單一例子舉出 demo 在幹嘛,
總之可以自己玩玩看 Layouts 這一塊,
先「拉一個 Layout」,再把「要排版的元件丟進去」,就對自動對整齊了!

  • Layouts 系列:

設定字體顏色,背景顏色的地方位於 styleSheet

使用的應該是類似 css 語法? 我不確定XD
總之有興趣可以直接參考下面的延伸閱讀進行修改,
設定位置在 Qt desinger 的這邊

延伸閱讀:給想研究更多怎麼設定顏色的人,請參考 Qt Style Sheets Examples

設定 Logo 的地方

Qlabel 就是一個我們可以輸入圖片的地方,
透過右邊的「...」,選擇一張我們要的圖片,就可以直接把 LOGO 嵌入 UI 中了。

執行看看 UI.py 畫面是否如同我們想像

一樣,這程式只有介面 (視覺上的呈現),沒有任何互動功能

  • 看看我們製作出來的介面
python UI.py

讚到不行XDDDD (自己講

程式架構篇

這次的 final project 真的內容會做太多,如果沒有好好規劃一下架構,
很有可能寫出來的東西會一團亂XDDD
所以我們要先趁今天來規劃,明天再來慢慢把功能實現

先從大方向切入,我們有那些大的「重要元素」?

依照 day5 的設計,我們至少也會有

  • UI (作為前端顯示介面)
  • controller (做為後端邏輯控制)
  • start (啟動用,沒什麼好討論的)

而 UI 因為我們在 Qtdesigner 已經設計得夠複雜了,也不太需要另外寫程式,
所以基本上在經由 pyuic 的轉換後,就已經完成了 UI.py

pyuic5 -x day28.ui -o UI.py

controller 是我們這邊要討論的重點,在我們的 photoshop 後端邏輯裡面,
我之前在 day17 實作的最終版是把「圖像+圖像變化方法」寫在同一個 class 當中,
我們如果繼續讓我們的「變化方法增加」,不適不能這樣實作,
但最終我們會有一個超級巨大的 class 共同實作 「圖像+圖像變化方法」,
於是我決定把「變化方法」拆出去獨立一個 class,
使得「圖像」與「變化方法」分開成兩套獨立機制,而能互向協助。

這也符合 design pattern 的 Interface Segregation Principle(ISP) 介面隔離原則
以遊戲來說,我們也可以舉例:
我們大可以把一個角色的所有「屬性、裝備、技能」全部都包在這個角色當中,
但如果另外一個玩家也玩了同一個職業,我們何必把技能「整個複製進去另外一個角色當中呢?
於是我們可以把「技能」這個介面分離出去,讓有需要的類別再去呼叫這個介面即可。

因此,這時候就能透過這樣的拆分方式,把我們實作的彈性與擴充功能的彈性提升。

  • 舉例:

套用 design pattern 前

套用 design pattern 前,基本上這個設計沒什麼問題,
硬要說缺點只是維護職業技能時,「需要一個個角色去調整內部的技能

套用 design pattern 後 (使用 Interface Segregation Principle(ISP) 介面隔離原則)

套用 design pattern 的 Interface Segregation Principle(ISP) 介面隔離原則後,
我們把技能獨立出來維護,這樣的好處就是我們可以調整一次職業技能,
所有的角色都能得到修正!

而這邊我們也要做同樣的事情,我們要把「修改圖片的方法」這個介面獨立出來!

於是整理一下,得到我們目前想設計的架構

獨立「圖片本身」與「圖片處理方法」,
另外因為滑鼠事件的資訊比較特別,需要從圖片上獲取,所以我們也另外獨立出來處理

【重要】抓出誰會是觸發事件的 trigger

第一次設計這個系統的時候,就碰到一個最關鍵的問題,
沒有提早先想好架構就開始寫,導致繞了很多彎路」,

應該要先想好再下去寫的問題就是,在這個系統裡面,「什麼事情會是觸發事件的 trigger?

如果我們不知道「什麼事件會被使用者觸發」,
就不知道要從哪個點開始啟動後續的修改」,

我一開始就是瘋狂地開始寫功能XDD,然後沒注意誰呼叫誰,
結果功能都有了,但是 trigger 位置寫在錯誤的 class,
所以要重搞順序XDD

以結論來說,這個系統的 trigger 位置如下

所以在寫系統時,我們需要特別注意,
我們箭頭指的地方就是「會觸發事件的 trigger」,
凡經過此處的程式都是「一次修改的起點」,引導我們進行往後的修改。

圖形修改介面 (interface) 設計

基本上我們設計的「修改圖片」都有符合一些同樣的原則,
我們可以使用「介面繼承」的方式來實現,
為了達到這樣的效果,我們需要 「import abc」 這個 python 酷酷的 library,
細節的部分我會再另外寫一篇文章,這邊我們先直接實作。

註:冷知識(?) abc = the infrastructure for defining 「abstract base classes (ABCs)」 in Python
簡單來說,就是「抽象的類別 (class)」定義

我們預計設計的架構如下,一共會有三層,底下會一層層介紹:

介面的大祖宗 (method_interface),一切介面方法從此開始實作

我們可以大致歸納我們全部的變化介面都會有兩個基本的要素:

  • 初始化參數: __init__
  • 更新圖片: update_img

另外,我們強制這兩個介面在繼承後都必須被定義,
所以我們透過「@abc.abstractmethod」,定義這個抽象的方法 (未實作功能),
以及「return NotImplemented」,如果使用者繼承介面後未定義這個函數,
會跳 「NotImplemented error」,強制跳錯 (逼使用者一定要定義介面內容)。

import abc

class method_interface(abc.ABC):
    @abc.abstractmethod
    def __init__(self):
        return NotImplemented

    @abc.abstractmethod
    def update_img(self):
        return NotImplemented 

介面的父母 (滑條方法介面 slider_method_interface, 畫筆方法介面 pen_method_interface)

我們會實作的「圖像變化方法」,能夠產生變化的方法大概有兩種,
一種是滑條類,另一種是畫筆類。

這兩類在實作時,都會繼承我們的介面大祖宗 (method_interface),
然後再分別基於滑條的特性與畫筆的特性,實作各自的介面。

這所有的介面方法都屬於「圖片變化方法」,所以我們會強烈推薦用繼承的方法撰寫。
會比起一個個慢慢定義有更好的架構與維護性
(維護一個父類別,也許底下的所有子類別都能全部受惠到這次的更動。不用一個個慢慢改。)

最大的差別就是:

  • 滑條拉回去後,可以復原 (不會是破壞原圖的變動)
  • 畫筆畫下去後,不可復原 (會破壞原圖的變動)

因此這兩個方法我們要分開實作,實作細節一樣我們明日再提,
今天光是講大架構就已經夠累了。

各個細部的方法實作

在看該方法是「滑條方法介面 slider_method_interface」或是「畫筆方法介面 pen_method_interface」後,
我們就可以開始實作細部功能了,這部分的實作細節我們就明日在提吧。
(今天光是講完大架構相信大家已經都累了XDDD)

最終系統架構圖

這個是舊版,滑鼠那部分因為是新加的,目前還沒更新上去,
不過相信上面已經說明得相當完整了,應該能大約類推出他在圖形上會呈現的細節

Reference


★ 本文也同步發於我的個人網站(會有內容目錄與顯示各個小節,閱讀起來更流暢):【PyQt5】Day 28 final project - 1 / 來搞一個自己的 photoshop 吧!UI 篇 + 純程式架構篇 (結合 PyQt + OpenCV)


上一篇
【沒錢買ps,PyQt自己寫】Day 27 - project / 製作影片 ROI 標註工具 (PyQt 結合 OpenCV 在圖上畫點畫線)
下一篇
【沒錢買ps,PyQt自己寫】Day 29 - final project - 2 / 來搞一個自己的 photoshop 吧!後段程式細節篇 (結合 PyQt + OpenCV)
系列文
【今年還是不夠錢買psQQ,不如我們用PyQt自己寫一個】30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

1 則留言

1
juck30808
iT邦研究生 1 級 ‧ 2021-10-14 12:13:42

恭喜即將邁入完賽~/images/emoticon/emoticon08.gif

嗡嗡 iT邦新手 4 級 ‧ 2021-10-14 22:26:49 檢舉

感謝!!! 一起加油拚完賽XDD

我要留言

立即登入留言